{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7a12c479",
   "metadata": {},
   "source": [
    "#  Protein Deep Learning \n",
    "by David Ricardo Figueroa Blanco"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f655918",
   "metadata": {},
   "source": [
    "In this tutorial we will  compare  protein sequences featurization  such as one hot encoders and aminoacids composition. We will use some tools of DeepChem and additional packages to create a model to predict melting temperature of proteins ( a good measurement of protein stability )    \n",
    "   \n",
    "The melting temperature (MT) of a protein is a measurement of protein stability. This measure could vary from a big variety of experimental conditions, however, curated databases cand be found in literature https://aip.scitation.org/doi/10.1063/1.4947493. In this paper we can find a lot of thermodynamic information of proteins and therefore a big resource for the study of protein stability. Other information related with protein stability could be the change in Gibbs Free Energy $ \\Delta \\Delta G°$ due to a mutation. \n",
    "\n",
    "The study of protein stability is important in areas such as protein engineering and biocatalysis because catalytic efficiency could be directly related to the tertiary structure of the protein in study."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8e6ed7a",
   "metadata": {},
   "source": [
    "# Setup\n",
    "To run DeepChem within Colab, you'll need to run the following installation commands. This will take about 5 minutes to run to completion and install your environment. You can of course run this tutorial locally if you prefer. In that case, don't run these cells since they will download and install Anaconda on your local machine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1ab8cfc",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl -Lo conda_installer.py https://raw.githubusercontent.com/deepchem/deepchem/master/scripts/colab_install.py\n",
    "import conda_installer\n",
    "conda_installer.install()\n",
    "!/root/miniconda/bin/conda info -e"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0ea929d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install --pre deepchem\n",
    "!pip install propy3 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3998a58",
   "metadata": {},
   "source": [
    "# Data extraction"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aabaaa7d",
   "metadata": {},
   "source": [
    "In this cell, we download the dataset published in the paper https://aip.scitation.org/doi/10.1063/1.4947493 from the DeepChem dataset repository"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b663d6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import deepchem as dc \n",
    "import os \n",
    "from deepchem.utils import download_url\n",
    "data_dir = dc.utils.get_data_dir()\n",
    "download_url(\"https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/pucci-proteins-appendixtable1.csv\",dest_dir=data_dir)\n",
    "print('Dataset Dowloaded at {}'.format(data_dir))\n",
    "dataset_file = os.path.join(data_dir, \"pucci-proteins-appendixtable1.csv\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52f2e6d2",
   "metadata": {},
   "source": [
    "A closer look of the dataset: Contains the PDBid and the respective mutation and change in thermodynamical properties in each studied protein"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "193aff48",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Unnamed: 0</th>\n",
       "      <th>N</th>\n",
       "      <th>PDBid</th>\n",
       "      <th>Chain</th>\n",
       "      <th>RESN</th>\n",
       "      <th>RESwt</th>\n",
       "      <th>RESmut</th>\n",
       "      <th>ΔTmexp</th>\n",
       "      <th>Tmexp [wt]</th>\n",
       "      <th>ΔΔHmexp</th>\n",
       "      <th>...</th>\n",
       "      <th>ΔΔGexp(T)</th>\n",
       "      <th>T</th>\n",
       "      <th>Nres</th>\n",
       "      <th>R (Å)</th>\n",
       "      <th>Protein</th>\n",
       "      <th>Organism</th>\n",
       "      <th>Ref.</th>\n",
       "      <th>pH</th>\n",
       "      <th>Exp.Tech.</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1</td>\n",
       "      <td>1aky</td>\n",
       "      <td>A</td>\n",
       "      <td>8</td>\n",
       "      <td>VAL</td>\n",
       "      <td>ILE</td>\n",
       "      <td>-1.5</td>\n",
       "      <td>47.6</td>\n",
       "      <td>70</td>\n",
       "      <td>...</td>\n",
       "      <td>5.0</td>\n",
       "      <td>25</td>\n",
       "      <td>220</td>\n",
       "      <td>1.63</td>\n",
       "      <td>ADK</td>\n",
       "      <td>Yeast</td>\n",
       "      <td>[1]</td>\n",
       "      <td>[7.5]</td>\n",
       "      <td>FL</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>NaN</td>\n",
       "      <td>2</td>\n",
       "      <td>1aky</td>\n",
       "      <td>A</td>\n",
       "      <td>48</td>\n",
       "      <td>GLN</td>\n",
       "      <td>GLU</td>\n",
       "      <td>-1.3</td>\n",
       "      <td>47.6</td>\n",
       "      <td>60</td>\n",
       "      <td>...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>25</td>\n",
       "      <td>220</td>\n",
       "      <td>1.63</td>\n",
       "      <td>ADK</td>\n",
       "      <td>Yeast</td>\n",
       "      <td>[1]</td>\n",
       "      <td>[7.7]</td>\n",
       "      <td>FL</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>NaN</td>\n",
       "      <td>3</td>\n",
       "      <td>1aky</td>\n",
       "      <td>A</td>\n",
       "      <td>77</td>\n",
       "      <td>THR</td>\n",
       "      <td>HIS</td>\n",
       "      <td>-1.1</td>\n",
       "      <td>47.6</td>\n",
       "      <td>130</td>\n",
       "      <td>...</td>\n",
       "      <td>9.0</td>\n",
       "      <td>25</td>\n",
       "      <td>220</td>\n",
       "      <td>1.63</td>\n",
       "      <td>ADK</td>\n",
       "      <td>Yeast</td>\n",
       "      <td>[1]</td>\n",
       "      <td>[7.5]</td>\n",
       "      <td>FL</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>NaN</td>\n",
       "      <td>4</td>\n",
       "      <td>1aky</td>\n",
       "      <td>A</td>\n",
       "      <td>110</td>\n",
       "      <td>THR</td>\n",
       "      <td>HIS</td>\n",
       "      <td>-4.8</td>\n",
       "      <td>47.6</td>\n",
       "      <td>165</td>\n",
       "      <td>...</td>\n",
       "      <td>11.0</td>\n",
       "      <td>25</td>\n",
       "      <td>220</td>\n",
       "      <td>1.63</td>\n",
       "      <td>ADK</td>\n",
       "      <td>Yeast</td>\n",
       "      <td>[1]</td>\n",
       "      <td>[7.6]</td>\n",
       "      <td>FL</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>NaN</td>\n",
       "      <td>5</td>\n",
       "      <td>1aky</td>\n",
       "      <td>A</td>\n",
       "      <td>169</td>\n",
       "      <td>ASN</td>\n",
       "      <td>ASP</td>\n",
       "      <td>-0.6</td>\n",
       "      <td>47.6</td>\n",
       "      <td>140</td>\n",
       "      <td>...</td>\n",
       "      <td>9.0</td>\n",
       "      <td>25</td>\n",
       "      <td>220</td>\n",
       "      <td>1.63</td>\n",
       "      <td>ADK</td>\n",
       "      <td>Yeast</td>\n",
       "      <td>[1]</td>\n",
       "      <td>[7.5]</td>\n",
       "      <td>FL</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1621</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1622</td>\n",
       "      <td>5pti_m52l</td>\n",
       "      <td>A</td>\n",
       "      <td>15</td>\n",
       "      <td>LYS</td>\n",
       "      <td>SER</td>\n",
       "      <td>-1.3</td>\n",
       "      <td>91.7</td>\n",
       "      <td>-5</td>\n",
       "      <td>...</td>\n",
       "      <td>1.2</td>\n",
       "      <td>25</td>\n",
       "      <td>58</td>\n",
       "      <td>1.00</td>\n",
       "      <td>PTI M52L</td>\n",
       "      <td>Bovine</td>\n",
       "      <td>[232]</td>\n",
       "      <td>[3.0]</td>\n",
       "      <td>DSC</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1622</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1623</td>\n",
       "      <td>5pti_m52l</td>\n",
       "      <td>A</td>\n",
       "      <td>15</td>\n",
       "      <td>LYS</td>\n",
       "      <td>THR</td>\n",
       "      <td>-1.1</td>\n",
       "      <td>91.7</td>\n",
       "      <td>-9</td>\n",
       "      <td>...</td>\n",
       "      <td>-3.6</td>\n",
       "      <td>25</td>\n",
       "      <td>58</td>\n",
       "      <td>1.00</td>\n",
       "      <td>PTI M52L</td>\n",
       "      <td>Bovine</td>\n",
       "      <td>[232]</td>\n",
       "      <td>[3.0]</td>\n",
       "      <td>DSC</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1623</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1624</td>\n",
       "      <td>5pti_m52l</td>\n",
       "      <td>A</td>\n",
       "      <td>15</td>\n",
       "      <td>LYS</td>\n",
       "      <td>VAL</td>\n",
       "      <td>-6.3</td>\n",
       "      <td>91.7</td>\n",
       "      <td>4</td>\n",
       "      <td>...</td>\n",
       "      <td>4.7</td>\n",
       "      <td>25</td>\n",
       "      <td>58</td>\n",
       "      <td>1.00</td>\n",
       "      <td>PTI M52L</td>\n",
       "      <td>Bovine</td>\n",
       "      <td>[232]</td>\n",
       "      <td>[3.0]</td>\n",
       "      <td>DSC</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1624</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1625</td>\n",
       "      <td>5pti_m52l</td>\n",
       "      <td>A</td>\n",
       "      <td>15</td>\n",
       "      <td>LYS</td>\n",
       "      <td>TRP</td>\n",
       "      <td>-7.5</td>\n",
       "      <td>91.7</td>\n",
       "      <td>17</td>\n",
       "      <td>...</td>\n",
       "      <td>8.5</td>\n",
       "      <td>25</td>\n",
       "      <td>58</td>\n",
       "      <td>1.00</td>\n",
       "      <td>PTI M52L</td>\n",
       "      <td>Bovine</td>\n",
       "      <td>[232]</td>\n",
       "      <td>[2.5]</td>\n",
       "      <td>DSC</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1625</th>\n",
       "      <td>NaN</td>\n",
       "      <td>1626</td>\n",
       "      <td>5pti_m52l</td>\n",
       "      <td>A</td>\n",
       "      <td>15</td>\n",
       "      <td>LYS</td>\n",
       "      <td>TYR</td>\n",
       "      <td>-6.6</td>\n",
       "      <td>91.7</td>\n",
       "      <td>4</td>\n",
       "      <td>...</td>\n",
       "      <td>4.6</td>\n",
       "      <td>25</td>\n",
       "      <td>58</td>\n",
       "      <td>1.00</td>\n",
       "      <td>PTI M52L</td>\n",
       "      <td>Bovine</td>\n",
       "      <td>[232]</td>\n",
       "      <td>[3.0]</td>\n",
       "      <td>DSC</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1626 rows × 23 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      Unnamed: 0     N      PDBid Chain  RESN RESwt RESmut  ΔTmexp  \\\n",
       "0            NaN     1       1aky     A     8   VAL    ILE    -1.5   \n",
       "1            NaN     2       1aky     A    48   GLN    GLU    -1.3   \n",
       "2            NaN     3       1aky     A    77   THR    HIS    -1.1   \n",
       "3            NaN     4       1aky     A   110   THR    HIS    -4.8   \n",
       "4            NaN     5       1aky     A   169   ASN    ASP    -0.6   \n",
       "...          ...   ...        ...   ...   ...   ...    ...     ...   \n",
       "1621         NaN  1622  5pti_m52l     A    15   LYS    SER    -1.3   \n",
       "1622         NaN  1623  5pti_m52l     A    15   LYS    THR    -1.1   \n",
       "1623         NaN  1624  5pti_m52l     A    15   LYS    VAL    -6.3   \n",
       "1624         NaN  1625  5pti_m52l     A    15   LYS    TRP    -7.5   \n",
       "1625         NaN  1626  5pti_m52l     A    15   LYS    TYR    -6.6   \n",
       "\n",
       "      Tmexp [wt] ΔΔHmexp  ... ΔΔGexp(T)   T Nres R (Å)   Protein  Organism  \\\n",
       "0           47.6      70  ...       5.0  25  220  1.63       ADK     Yeast   \n",
       "1           47.6      60  ...       4.0  25  220  1.63       ADK     Yeast   \n",
       "2           47.6     130  ...       9.0  25  220  1.63       ADK     Yeast   \n",
       "3           47.6     165  ...      11.0  25  220  1.63       ADK     Yeast   \n",
       "4           47.6     140  ...       9.0  25  220  1.63       ADK     Yeast   \n",
       "...          ...     ...  ...       ...  ..  ...   ...       ...       ...   \n",
       "1621        91.7      -5  ...       1.2  25   58  1.00  PTI M52L    Bovine   \n",
       "1622        91.7      -9  ...      -3.6  25   58  1.00  PTI M52L    Bovine   \n",
       "1623        91.7       4  ...       4.7  25   58  1.00  PTI M52L    Bovine   \n",
       "1624        91.7      17  ...       8.5  25   58  1.00  PTI M52L    Bovine   \n",
       "1625        91.7       4  ...       4.6  25   58  1.00  PTI M52L    Bovine   \n",
       "\n",
       "       Ref.     pH Exp.Tech.      \n",
       "0       [1]  [7.5]       FL  NaN  \n",
       "1       [1]  [7.7]       FL  NaN  \n",
       "2       [1]  [7.5]       FL  NaN  \n",
       "3       [1]  [7.6]       FL  NaN  \n",
       "4       [1]  [7.5]       FL  NaN  \n",
       "...     ...    ...       ...  ..  \n",
       "1621  [232]  [3.0]       DSC NaN  \n",
       "1622  [232]  [3.0]       DSC NaN  \n",
       "1623  [232]  [3.0]       DSC NaN  \n",
       "1624  [232]  [2.5]       DSC NaN  \n",
       "1625  [232]  [3.0]       DSC NaN  \n",
       "\n",
       "[1626 rows x 23 columns]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd \n",
    "data = pd.read_csv(dataset_file)\n",
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12854864",
   "metadata": {},
   "source": [
    "Here we extract a small DataFrame that only contains a unique PDBid code and its respective melting temperature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ed716019",
   "metadata": {},
   "outputs": [],
   "source": [
    "WT_Tm = data[['PDBid','Tmexp [wt]']]\n",
    "WT_Tm.set_index('PDBid',inplace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a08b3599",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Tmexp [wt]</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>PDBid</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1aky</th>\n",
       "      <td>47.6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1aky</th>\n",
       "      <td>47.6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1aky</th>\n",
       "      <td>47.6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1aky</th>\n",
       "      <td>47.6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1aky</th>\n",
       "      <td>47.6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5pti_m52l</th>\n",
       "      <td>91.7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5pti_m52l</th>\n",
       "      <td>91.7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5pti_m52l</th>\n",
       "      <td>91.7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5pti_m52l</th>\n",
       "      <td>91.7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5pti_m52l</th>\n",
       "      <td>91.7</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1626 rows × 1 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "           Tmexp [wt]\n",
       "PDBid                \n",
       "1aky             47.6\n",
       "1aky             47.6\n",
       "1aky             47.6\n",
       "1aky             47.6\n",
       "1aky             47.6\n",
       "...               ...\n",
       "5pti_m52l        91.7\n",
       "5pti_m52l        91.7\n",
       "5pti_m52l        91.7\n",
       "5pti_m52l        91.7\n",
       "5pti_m52l        91.7\n",
       "\n",
       "[1626 rows x 1 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "WT_Tm"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01718488",
   "metadata": {},
   "source": [
    "Here we create a dictionary that contains as keys, the pdbid of each protein and  as values, the wild type melting temperature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9e424a63",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "dict_WT_TM = {}\n",
    "for k,v in WT_Tm.itertuples():\n",
    "    if(k not in dict_WT_TM):\n",
    "        dict_WT_TM[k]=float(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ea713f5",
   "metadata": {},
   "source": [
    "Here we extract proteins with mutations reported only in chain A "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4d07c8dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "pdbs = data[data['PDBid'].str.len()<5]\n",
    "pdbs = pdbs[pdbs['Chain'] == \"A\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a1140b30",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>RESN</th>\n",
       "      <th>RESwt</th>\n",
       "      <th>RESmut</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>8</td>\n",
       "      <td>VAL</td>\n",
       "      <td>ILE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>48</td>\n",
       "      <td>GLN</td>\n",
       "      <td>GLU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>77</td>\n",
       "      <td>THR</td>\n",
       "      <td>HIS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>110</td>\n",
       "      <td>THR</td>\n",
       "      <td>HIS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>169</td>\n",
       "      <td>ASN</td>\n",
       "      <td>ASP</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1604</th>\n",
       "      <td>36</td>\n",
       "      <td>GLY</td>\n",
       "      <td>ALA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1605</th>\n",
       "      <td>36</td>\n",
       "      <td>GLY</td>\n",
       "      <td>SER</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1606</th>\n",
       "      <td>37</td>\n",
       "      <td>GLY</td>\n",
       "      <td>ALA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1607</th>\n",
       "      <td>39</td>\n",
       "      <td>ARG</td>\n",
       "      <td>ALA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1608</th>\n",
       "      <td>46</td>\n",
       "      <td>LYS</td>\n",
       "      <td>ALA</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1509 rows × 3 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      RESN RESwt RESmut\n",
       "0        8   VAL    ILE\n",
       "1       48   GLN    GLU\n",
       "2       77   THR    HIS\n",
       "3      110   THR    HIS\n",
       "4      169   ASN    ASP\n",
       "...    ...   ...    ...\n",
       "1604    36   GLY    ALA\n",
       "1605    36   GLY    SER\n",
       "1606    37   GLY    ALA\n",
       "1607    39   ARG    ALA\n",
       "1608    46   LYS    ALA\n",
       "\n",
       "[1509 rows x 3 columns]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pdbs[['RESN','RESwt','RESmut']]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b34b45c",
   "metadata": {},
   "source": [
    "This cell extracts the total number of mutations and changes in MT. In addition, we use a dicctionary to convert the residue mutation in a one letter code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "eaedf902",
   "metadata": {},
   "outputs": [],
   "source": [
    "alls=[]\n",
    "for resnum,wt in pdbs[['RESN','RESwt','RESmut','PDBid','ΔTmexp']].items():\n",
    "    alls.append(wt.values)\n",
    "d = {'CYS': 'C', 'ASP': 'D', 'SER': 'S', 'GLN': 'Q', 'LYS': 'K',\n",
    "     'ILE': 'I', 'PRO': 'P', 'THR': 'T', 'PHE': 'F', 'ASN': 'N', \n",
    "     'GLY': 'G', 'HIS': 'H', 'LEU': 'L', 'ARG': 'R', 'TRP': 'W', \n",
    "     'ALA': 'A', 'VAL':'V', 'GLU': 'E', 'TYR': 'Y', 'MET': 'M'}\n",
    "resnum=alls[0]\n",
    "wt=[d[x.strip()] for x in alls[1]] # extract the Wildtype aminoacid with one letter code \n",
    "mut=[d[x.strip()] for x in alls[2]] # extract the Mutation aminoacid with one letter code \n",
    "codes=alls[3] # PDB code \n",
    "tms=alls[4] # Melting temperature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fb71eb03",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pdbid 1aky, WT-AA V, Resnum 8, MUT-AA I,DeltaTm -1.5\n"
     ]
    }
   ],
   "source": [
    "print(\"pdbid {}, WT-AA {}, Resnum {}, MUT-AA {},DeltaTm {}\".format(codes[0],wt[0],resnum[0],mut[0],tms[0]))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c61049f",
   "metadata": {},
   "source": [
    "# PDB Download "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c87b9d53",
   "metadata": {},
   "source": [
    "Here we download all the pdbs by PDBID using the pdbfixer tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "9e315469",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pdbfixer import PDBFixer\n",
    "from openmm.app import PDBFile"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1c11e21",
   "metadata": {},
   "outputs": [],
   "source": [
    "!mkdir PDBs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "137eb8f6",
   "metadata": {},
   "source": [
    "Using the fixer from pdbfixer we download each protein from its PDB code and fix some common problems present in the Protein Data Bank Files. This process will take around 15 minutes and 100 Mb. \n",
    "The use of the PDBFixer can be found in https://htmlpreview.github.io/?https://github.com/openmm/pdbfixer/blob/master/Manual.html . In our case, we download the pdb file from the pdb code and perform some curation such as find Nonstandar or missing residues, fix missing atoms "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67164bf6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os \n",
    "import time\n",
    "t0 = time.time()\n",
    "\n",
    "downloaded = os.listdir(\"PDBs\")\n",
    "PDBs_ids= set(pdbs['PDBid'])\n",
    "pdb_list = []\n",
    "print(\"Start Download \")\n",
    "for pdbid in PDBs_ids:\n",
    "    name=pdbid+\".pdb\"\n",
    "    if(name in downloaded):\n",
    "        continue\n",
    "    try:\n",
    "        fixer = PDBFixer(pdbid=pdbid)\n",
    "        fixer.findMissingResidues()\n",
    "        fixer.findNonstandardResidues()\n",
    "        fixer.replaceNonstandardResidues()\n",
    "        fixer.removeHeterogens(True)\n",
    "        fixer.findMissingAtoms()\n",
    "        fixer.addMissingAtoms()\n",
    "        PDBFile.writeFile(fixer.topology, fixer.positions, open('./PDBs/%s.pdb' % (pdbid), 'w'),keepIds=True)\n",
    "    except:\n",
    "        print(\"Problem with {}\".format(pdbid))\n",
    "print(\"Total Time {}\".format(time.time()-t0))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c1e8d69",
   "metadata": {},
   "source": [
    "The following function help us to mutate a sequence denoted as  A###B where A is the wildtype aminoacid, ### the position and, B the new aminoacid "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "6ae88b01",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "def MutateSeq(seq,Mutant):\n",
    "    '''\n",
    "    Mutate a sequence based on a string (Mutant) that has the notation : \n",
    "    A###B where A is the wildtype aminoacid ### the position and B the mutation\n",
    "    '''\n",
    "    aalist = re.findall('([A-Z])([0-9]+)([A-Z])', Mutant)\n",
    "    \n",
    "    #(len(aalist)==1):\n",
    "    newseq=seq\n",
    "    listseq=list(newseq)\n",
    "    for aas in aalist:\n",
    "        wildAA = aas[0]\n",
    "        pos = int(aas[1]) -1\n",
    "        if(pos >= len(listseq)):\n",
    "            print(\"Mutation not in the range of the protein\")\n",
    "            return None\n",
    "        MutAA = aas[-1]\n",
    "        \n",
    "        if(listseq[pos]==wildAA):\n",
    "            \n",
    "            listseq[pos]=MutAA\n",
    "            \n",
    "        else:\n",
    "            #print(\"WildType AA does not match\")\n",
    "            return None\n",
    "    return(\"\".join(listseq))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb1a5f04",
   "metadata": {},
   "source": [
    "The following function help us to identify a sequence of aminoacids base on PDB structures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "39facf99",
   "metadata": {},
   "outputs": [],
   "source": [
    "from Bio.PDB.PDBParser import PDBParser\n",
    "from Bio.PDB.Polypeptide import PPBuilder\n",
    "ppb=PPBuilder()\n",
    "def GetSeqFromPDB(pdbid):\n",
    "    structure = PDBParser().get_structure(pdbid.split(\".\")[0], 'PDBs/{}'.format(pdbid))\n",
    "    seqs=[]\n",
    "    return ppb.build_peptides(structure)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9064f9af",
   "metadata": {},
   "source": [
    "Some examples of the described functions : \n",
    "GetSeqFromPDB. Take one pdb that we previously downloaded and extract the sequence in one letter code "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "5182b09e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1ezm\n",
      "Original Sequence\n",
      "AEAGGPGGNQKIGKYTYGSDYGPLIVNDRCEMDDGNVITVDMNSSTDDSKTTPFRFACPTNTYKQVNGAYSPLNDAHFFGGVVFKLYRDWFGTSPLTHKLYMKVHYGRSVENAYWDGTAMLFGDGATMFYPLVSLDVAAHEVSHGFTEQNSGLIYRGQSGGMNEAFSDMAGEAAEFYMRGKNDFLIGYDIKKGSGALRYMDQPSRDGRSIDNASQYYNGIDVHHSSGVYNRAFYLLANSPGWDTRKAFEVFVDANRYYWTATSNYNSGACGVIRSAQNRNYSAADVTRAFSTVGVTCPSAL\n"
     ]
    }
   ],
   "source": [
    "import warnings; warnings.simplefilter('ignore')\n",
    "test=\"1ezm\"\n",
    "print(test)\n",
    "seq = GetSeqFromPDB(\"{}.pdb\".format(test))[0].get_sequence()\n",
    "print(\"Original Sequence\")\n",
    "print(seq)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b624263",
   "metadata": {},
   "source": [
    "Information about the mutation "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "ddeeb4d6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Seq information <Polypeptide start=1 end=301>\n",
      "Reported Mutation R179A\n"
     ]
    }
   ],
   "source": [
    "informSeq=GetSeqFromPDB(test+\".pdb\")[0].__repr__()\n",
    "print(\"Seq information\",informSeq)\n",
    "start = re.findall('[0-9]+',informSeq)[0]\n",
    "print(\"Reported Mutation {}{}{}\".format(\"R\",179,\"A\"))\n",
    "numf =179 - int(start) + 1  # fix some cases of negative aminoacid numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fed72d0",
   "metadata": {},
   "source": [
    "Mutation in the sequence. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "58d4cdf2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Real Mutation =  R179A\n",
      "AEAGGPGGNQKIGKYTYGSDYGPLIVNDRCEMDDGNVITVDMNSSTDDSKTTPFRFACPTNTYKQVNGAYSPLNDAHFFGGVVFKLYRDWFGTSPLTHKLYMKVHYGRSVENAYWDGTAMLFGDGATMFYPLVSLDVAAHEVSHGFTEQNSGLIYRGQSGGMNEAFSDMAGEAAEFYMAGKNDFLIGYDIKKGSGALRYMDQPSRDGRSIDNASQYYNGIDVHHSSGVYNRAFYLLANSPGWDTRKAFEVFVDANRYYWTATSNYNSGACGVIRSAQNRNYSAADVTRAFSTVGVTCPSAL\n"
     ]
    }
   ],
   "source": [
    "mutfinal = \"R{}A\".format(numf)\n",
    "print(\"Real Mutation = \",mutfinal)\n",
    "mutseq = MutateSeq(seq,mutfinal)\n",
    "print(mutseq)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68f84f92",
   "metadata": {},
   "source": [
    "In this for loop we extract the sequences of all proteins in the dataset. In addition we created the mutated sequences and append the change in MT. In some cases, gaps in pdbs will cause that mutateSeq function fails, therefore this entries will be avoided. This is an important step in the whole process because creates a final tabulated data that contains the sequence and the Melting temperature ( our label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52a4fbcd",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "information = {}\n",
    "count = 1\n",
    "failures=[]\n",
    "for code,tm,numr,wt_val,mut_val in zip(codes,tms,resnum,wt,mut):\n",
    "    count += 1\n",
    "    seq = GetSeqFromPDB(\"{}.pdb\".format(code))[0].get_sequence()\n",
    "    mutfinal=\"WT\"\n",
    "    if(\"{}-{}\".format(code,mutfinal) not in information):\n",
    "        informSeq=GetSeqFromPDB(code+\".pdb\")[0].__repr__()\n",
    "        start = re.findall('[-0-9]+',informSeq)[0]\n",
    "    if(int(start)<0):\n",
    "        numf =numr - int(start) # if start is negative 0 is not used as resnumber\n",
    "    else:\n",
    "        numf =numr - int(start) + 1 \n",
    "    mutfinal = \"{}{}{}\".format(wt_val,numf,mut_val)\n",
    "    mutseq = MutateSeq(seq,mutfinal)\n",
    "    if(mutseq==None):\n",
    "        failures.append((code,mutfinal))\n",
    "        continue\n",
    "    information[\"{}-{}\".format(code,mutfinal)]=[mutseq,dict_WT_TM[code]-float(tm)]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c40b5c3",
   "metadata": {},
   "source": [
    "# Deep Learning and Machine Learning Models using proteins sequences "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "79b561c7",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import deepchem as dc\n",
    "import torch.nn as nn\n",
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f023dd58",
   "metadata": {},
   "source": [
    "Here we extract two list, sequences (data) and  melting temperature (label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "a53ed3ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "seq_list=[]\n",
    "deltaTm=[]\n",
    "for i in information.values():\n",
    "    seq_list.append(i[0])\n",
    "    deltaTm.append(i[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "9bc80e01",
   "metadata": {},
   "outputs": [],
   "source": [
    "max_seq= 0 \n",
    "for i in seq_list:\n",
    "    if(len(i)>max_seq):\n",
    "        max_seq=len(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efce9af0",
   "metadata": {},
   "source": [
    "Here we use a OneHotFeaturizer in order to convert protein sequences in numeric arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "3994b2b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "codes = ['A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L',\n",
    "         'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y']\n",
    "OneHotFeaturizer = dc.feat.OneHotFeaturizer(codes,max_length=max_seq)\n",
    "features = OneHotFeaturizer.featurize(seq_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ed774a5-7365-4540-ab4f-0996196d7576",
   "metadata": {},
   "source": [
    "Note that the OneHotFeaturizer produces a matrix that contains the OneHot Vector for each sequence. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "9d052022-ac12-428c-b52a-0e6dc58474e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "features_vector  = []\n",
    "for i in range(len(features)):\n",
    "    features_vector.append(features[i].flatten())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "544fcad7",
   "metadata": {},
   "outputs": [],
   "source": [
    "dc_dataset = dc.data.NumpyDataset(X=features_vector,y=deltaTm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "dd02326a-cac3-4fca-9f8f-064ba223505d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<NumpyDataset X.shape: (1488, 13188), y.shape: (1488,), w.shape: (1488,), task_names: [0]>"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dc_dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13160072",
   "metadata": {},
   "source": [
    "Here we create a spliiter to perform a tran_test split of the dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "fe8c1fc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from deepchem import splits\n",
    "splitter = splits.RandomSplitter()\n",
    "train, test  = splitter.train_test_split(dc_dataset,seed=42)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c83cac17-74ba-4af8-a2e1-d5091f8a1f88",
   "metadata": {},
   "source": [
    "Here we create a neuronal network using tensorflow-keras and using a loss function of \"MAE\" to evaluate the regression result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "ff35ae79-46e4-40da-a0e8-b8e95a457787",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.optim as optim\n",
    "import torch.nn.functional as F\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "\n",
    "# Define the Model\n",
    "class ProteinModel(nn.Module):\n",
    "    def __init__(self, input_size):\n",
    "        super(ProteinModel, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, 32)\n",
    "        self.dropout1 = nn.Dropout(0.2)\n",
    "        self.fc2 = nn.Linear(32, 32)\n",
    "        self.dropout2 = nn.Dropout(0.2)\n",
    "        self.fc3 = nn.Linear(32, 1)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = self.dropout1(x)\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = self.dropout2(x)\n",
    "        x = self.fc3(x)\n",
    "        return x\n",
    "\n",
    "input_size = train.X.shape[1]  \n",
    "model = ProteinModel(input_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "8542bb35-90e0-4c43-a7af-40ad1185d639",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/30], Train Loss: 59.8350, Val Loss: 61.0038\n",
      "Epoch [2/30], Train Loss: 51.4501, Val Loss: 53.2404\n",
      "Epoch [3/30], Train Loss: 42.1209, Val Loss: 38.0507\n",
      "Epoch [4/30], Train Loss: 18.6921, Val Loss: 13.0056\n",
      "Epoch [5/30], Train Loss: 15.8223, Val Loss: 11.1212\n",
      "Epoch [6/30], Train Loss: 14.8701, Val Loss: 9.4398\n",
      "Epoch [7/30], Train Loss: 16.6456, Val Loss: 9.0781\n",
      "Epoch [8/30], Train Loss: 13.1553, Val Loss: 7.4179\n",
      "Epoch [9/30], Train Loss: 13.4347, Val Loss: 6.9786\n",
      "Epoch [10/30], Train Loss: 12.7587, Val Loss: 7.1300\n",
      "Epoch [11/30], Train Loss: 12.4871, Val Loss: 6.9377\n",
      "Epoch [12/30], Train Loss: 12.3102, Val Loss: 6.5128\n",
      "Epoch [13/30], Train Loss: 11.6099, Val Loss: 7.5611\n",
      "Epoch [14/30], Train Loss: 14.1950, Val Loss: 6.7952\n",
      "Epoch [15/30], Train Loss: 10.5894, Val Loss: 6.1077\n",
      "Epoch [16/30], Train Loss: 13.2864, Val Loss: 6.8252\n",
      "Epoch [17/30], Train Loss: 11.8302, Val Loss: 6.4149\n",
      "Epoch [18/30], Train Loss: 10.8937, Val Loss: 6.6818\n",
      "Epoch [19/30], Train Loss: 10.8588, Val Loss: 5.9689\n",
      "Epoch [20/30], Train Loss: 11.2092, Val Loss: 7.1350\n",
      "Epoch [21/30], Train Loss: 10.6252, Val Loss: 6.6436\n",
      "Epoch [22/30], Train Loss: 11.2696, Val Loss: 8.6385\n",
      "Epoch [23/30], Train Loss: 13.0959, Val Loss: 5.7105\n",
      "Epoch [24/30], Train Loss: 10.6112, Val Loss: 7.7095\n",
      "Epoch [25/30], Train Loss: 9.7773, Val Loss: 8.0182\n",
      "Epoch [26/30], Train Loss: 11.1502, Val Loss: 8.1011\n",
      "Epoch [27/30], Train Loss: 9.5145, Val Loss: 8.9478\n",
      "Epoch [28/30], Train Loss: 10.8732, Val Loss: 10.6348\n",
      "Epoch [29/30], Train Loss: 8.9625, Val Loss: 8.5645\n",
      "Epoch [30/30], Train Loss: 9.7106, Val Loss: 9.4346\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjQAAAHHCAYAAACoZcIpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAcYhJREFUeJzt3Xd4VGXexvHvpE167xB66EVAhIgUBUVUBMGOCjYs2HVXfV0VXVdcde2KjUV3V0RRwQoICKgIKB3pgRBaCgmk98x5/zjJkNBTJzO5P9c1V2bOnDnzm8nA3HnOUyyGYRiIiIiIODE3RxcgIiIiUlcKNCIiIuL0FGhERETE6SnQiIiIiNNToBERERGnp0AjIiIiTk+BRkRERJyeAo2IiIg4PQUaERERcXoKNCINZOLEibRp06ZWj50yZQoWi6V+C2pi9uzZg8Vi4aOPPmr057ZYLEyZMsV++6OPPsJisbBnz57TPrZNmzZMnDixXuupy2dFREwKNNLsWCyWM7osXbrU0aU2e/fddx8Wi4XExMST7vPEE09gsVjYuHFjI1ZWcwcPHmTKlCmsX7/e0aXYVYbKl19+2dGliNSZh6MLEGls//3vf6vd/s9//sPChQuP296lS5c6Pc8HH3yAzWar1WP/9re/8dhjj9Xp+V3B+PHjefPNN5k5cyZPPfXUCff59NNP6dGjBz179qz189x4441ce+21WK3WWh/jdA4ePMgzzzxDmzZtOOuss6rdV5fPioiYFGik2bnhhhuq3V65ciULFy48bvuxCgoK8PX1PePn8fT0rFV9AB4eHnh46J9n//796dChA59++ukJA82KFStISkrihRdeqNPzuLu74+7uXqdj1EVdPisiYtIpJ5ETGDp0KN27d2fNmjUMHjwYX19f/u///g+Ar7/+mksvvZTY2FisVivt27fn73//O+Xl5dWOcWy/iKrN+++//z7t27fHarXSr18//vjjj2qPPVEfGovFwj333MPcuXPp3r07VquVbt26MX/+/OPqX7p0KWeffTbe3t60b9+e995774z75fzyyy9cddVVtGrVCqvVSlxcHA8++CCFhYXHvT5/f38OHDjAmDFj8Pf3JyIigkceeeS49yIrK4uJEycSFBREcHAwEyZMICsr67S1gNlKs23bNtauXXvcfTNnzsRisXDddddRUlLCU089Rd++fQkKCsLPz49BgwaxZMmS0z7HifrQGIbBc889R8uWLfH19eX8889n8+bNxz328OHDPPLII/To0QN/f38CAwMZOXIkGzZssO+zdOlS+vXrB8DNN99sP61Z2X/oRH1o8vPzefjhh4mLi8NqtdKpUydefvllDMOotl9NPhe1lZ6ezq233kpUVBTe3t706tWLjz/++Lj9Zs2aRd++fQkICCAwMJAePXrw+uuv2+8vLS3lmWeeIT4+Hm9vb8LCwjjvvPNYuHBhvdUqzZf+BBQ5iczMTEaOHMm1117LDTfcQFRUFGB++fn7+/PQQw/h7+/PTz/9xFNPPUVOTg4vvfTSaY87c+ZMcnNzueOOO7BYLLz44ouMHTuW3bt3n/Yv9V9//ZWvvvqKu+++m4CAAN544w3GjRvH3r17CQsLA2DdunVcfPHFxMTE8Mwzz1BeXs6zzz5LRETEGb3u2bNnU1BQwF133UVYWBi///47b775Jvv372f27NnV9i0vL2fEiBH079+fl19+mUWLFvGvf/2L9u3bc9dddwFmMBg9ejS//vord955J126dGHOnDlMmDDhjOoZP348zzzzDDNnzqRPnz7Vnvvzzz9n0KBBtGrVioyMDD788EOuu+46br/9dnJzc5k+fTojRozg999/P+40z+k89dRTPPfcc1xyySVccsklrF27losuuoiSkpJq++3evZu5c+dy1VVX0bZtW9LS0njvvfcYMmQIW7ZsITY2li5duvDss8/y1FNPMWnSJAYNGgTAueeee8LnNgyDyy+/nCVLlnDrrbdy1llnsWDBAv7yl79w4MABXn311Wr7n8nnorYKCwsZOnQoiYmJ3HPPPbRt25bZs2czceJEsrKyuP/++wFYuHAh1113HcOGDeOf//wnAFu3bmX58uX2faZMmcLUqVO57bbbOOecc8jJyWH16tWsXbuWCy+8sE51imCINHOTJ082jv2nMGTIEAMw3n333eP2LygoOG7bHXfcYfj6+hpFRUX2bRMmTDBat25tv52UlGQARlhYmHH48GH79q+//toAjG+//da+7emnnz6uJsDw8vIyEhMT7ds2bNhgAMabb75p3zZq1CjD19fXOHDggH3bzp07DQ8Pj+OOeSInen1Tp041LBaLkZycXO31Acazzz5bbd/evXsbffv2td+eO3euARgvvviifVtZWZkxaNAgAzBmzJhx2pr69etntGzZ0igvL7dvmz9/vgEY7733nv2YxcXF1R535MgRIyoqyrjllluqbQeMp59+2n57xowZBmAkJSUZhmEY6enphpeXl3HppZcaNpvNvt///d//GYAxYcIE+7aioqJqdRmG+bu2Wq3V3ps//vjjpK/32M9K5Xv23HPPVdvvyiuvNCwWS7XPwJl+Lk6k8jP50ksvnXSf1157zQCM//3vf/ZtJSUlRkJCguHv72/k5OQYhmEY999/vxEYGGiUlZWd9Fi9evUyLr300lPWJFJbOuUkchJWq5Wbb775uO0+Pj7267m5uWRkZDBo0CAKCgrYtm3baY97zTXXEBISYr9d+df67t27T/vY4cOH0759e/vtnj17EhgYaH9seXk5ixYtYsyYMcTGxtr369ChAyNHjjzt8aH668vPzycjI4Nzzz0XwzBYt27dcfvfeeed1W4PGjSo2mv54Ycf8PDwsLfYgNln5d577z2jesDs97R//35+/vln+7aZM2fi5eXFVVddZT+ml5cXADabjcOHD1NWVsbZZ599wtNVp7Jo0SJKSkq49957q52me+CBB47b12q14uZm/ldaXl5OZmYm/v7+dOrUqcbPW+mHH37A3d2d++67r9r2hx9+GMMwmDdvXrXtp/tc1MUPP/xAdHQ01113nX2bp6cn9913H3l5eSxbtgyA4OBg8vPzT3n6KDg4mM2bN7Nz58461yVyLAUakZNo0aKF/Quyqs2bN3PFFVcQFBREYGAgERER9g7F2dnZpz1uq1atqt2uDDdHjhyp8WMrH1/52PT0dAoLC+nQocNx+51o24ns3buXiRMnEhoaau8XM2TIEOD41+ft7X3cqayq9QAkJycTExODv79/tf06dep0RvUAXHvttbi7uzNz5kwAioqKmDNnDiNHjqwWDj/++GN69uxp758RERHB999/f0a/l6qSk5MBiI+Pr7Y9IiKi2vOBGZ5effVV4uPjsVqthIeHExERwcaNG2v8vFWfPzY2loCAgGrbK0feVdZX6XSfi7pITk4mPj7eHtpOVsvdd99Nx44dGTlyJC1btuSWW245rh/Ps88+S1ZWFh07dqRHjx785S9/afLD7cV5KNCInETVlopKWVlZDBkyhA0bNvDss8/y7bffsnDhQnufgTMZenuy0TTGMZ096/uxZ6K8vJwLL7yQ77//nkcffZS5c+ey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QEREmisFGmdVrYXGnC1YLTQiItJcKdA4q8pAU5RFCz/zqjoFi4hIc6VA46y8g8DDbJmJ9cgGFGhERKT5UqBxVhaLvR9NJEcAyC0uI7dIk+uJiEjzU6tAs2/fPvbv32+//fvvv/PAAw/w/vvv11thcgYqTjv5FKUT6O0BQJqGbouISDNUq0Bz/fXXs2TJEgBSU1O58MIL+f3333niiSd49tln67VAOQX7XDQpxKhjsIiINGO1CjR//vkn55xzDgCff/453bt357fffuOTTz7ho48+qs/65FSqjHSK0lw0IiLSjNUq0JSWlmK1WgFYtGgRl19+OQCdO3cmJSWl/qqTUwusDDSpxGj5AxERacZqFWi6devGu+++yy+//MLChQu5+OKLATh48CBhYWH1WqCcQsDRQKPZgkVEpDmrVaD55z//yXvvvcfQoUO57rrr6NWrFwDffPON/VSUNIJqfWg0W7CIiDRfHrV50NChQ8nIyCAnJ4eQkBD79kmTJuHr61tvxclpqIVGREQEqGULTWFhIcXFxfYwk5yczGuvvcb27duJjIys1wLlFPyjzJ8lecT6lANacVtERJqnWgWa0aNH85///AeArKws+vfvz7/+9S/GjBnDtGnT6rVAOQWrP1gDAYhxMyfXyyoopbCk3JFViYiINLpaBZq1a9cyaNAgAL744guioqJITk7mP//5D2+88Ua9FiinUdGPxr8kHV8vd0CtNCIi0vzUKtAUFBQQEBAAwI8//sjYsWNxc3NjwIABJCcn12uBchoV/WgsuWlV+tGoY7CIiDQvtQo0HTp0YO7cuezbt48FCxZw0UUXAZCenk5gYGC9FiinUWVyvaMjndRCIyIizUutAs1TTz3FI488Qps2bTjnnHNISEgAzNaa3r1712uBchr2odupRAdq+QMREWmeajVs+8orr+S8884jJSXFPgcNwLBhw7jiiivqrTg5A2qhERERqV2gAYiOjiY6Otq+6nbLli01qZ4jVJlcL7qV5qIREZHmqVannGw2G88++yxBQUG0bt2a1q1bExwczN///ndsNlt91yincqIWmhx1ChYRkealVi00TzzxBNOnT+eFF15g4MCBAPz6669MmTKFoqIi/vGPf9RrkXIKVRaojA40FwzVKScREWluahVoPv74Yz788EP7KtsAPXv2pEWLFtx9990KNI2pcrbg8hJi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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "criterion = nn.L1Loss()  # Mean Absolute Error (MAE) Loss\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# Track loss history for plotting\n",
    "train_losses = []\n",
    "val_losses = []\n",
    "\n",
    "# Training Loop\n",
    "num_epochs = 30\n",
    "batch_size = 100\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    permutation = torch.randperm(train.X.shape[0])  # Shuffle indices for batch training\n",
    "    for i in range(0, train.X.shape[0], batch_size):\n",
    "        indices = permutation[i:i + batch_size]\n",
    "        batch_x = torch.tensor(train.X[indices], dtype=torch.float32)\n",
    "        batch_y = torch.tensor(train.y[indices], dtype=torch.float32).view(-1, 1)\n",
    "\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(batch_x)\n",
    "        loss = criterion(outputs, batch_y)\n",
    "\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "    train_losses.append(loss.item())\n",
    "\n",
    "    # Compute validation loss\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        val_x = torch.tensor(test.X, dtype=torch.float32)\n",
    "        val_y = torch.tensor(test.y, dtype=torch.float32).view(-1, 1)\n",
    "        val_outputs = model(val_x)\n",
    "        val_loss = criterion(val_outputs, val_y)\n",
    "        val_losses.append(val_loss.item())\n",
    "\n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {loss.item():.4f}, Val Loss: {val_loss.item():.4f}')\n",
    "\n",
    "# Plot the loss curve\n",
    "history_df = pd.DataFrame({\n",
    "    'loss': train_losses,\n",
    "    'val_loss': val_losses\n",
    "})\n",
    "history_df[['loss', 'val_loss']].plot()\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Validation Loss')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5150c028-6bdc-4377-9174-16f45a36df38",
   "metadata": {},
   "source": [
    "# DeepChem TorchModel Model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbc43f5d-679b-459a-b6ce-c0fbd4975d07",
   "metadata": {},
   "source": [
    "Note that DeepChem Torch model allows to create a deepchem model based on the previously built model of Pytorch. All the information in the training can be access with model.model.history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "d25092a4-e411-4179-acd2-d6d59975967b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10.934407043457032"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_dc = dc.models.TorchModel(model, dc.models.losses.L1Loss())\n",
    "model_dc.fit(train)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "6cf55cb2-7c18-4ae1-8654-fedc7e252a6d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<AxesSubplot:>"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "History_df = pd.DataFrame(model_dc.model.history.history)\n",
    "History_df[['loss', 'val_loss']].plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "ac12fbcc-f812-4e13-8c01-520b30267706",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test dataset R2: {'pearson_r2_score': 0.6921896004241516}\n"
     ]
    }
   ],
   "source": [
    "metric = dc.metrics.Metric(dc.metrics.pearson_r2_score)\n",
    "print('test dataset R2:', model_dc.evaluate(test, [metric]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9423a267",
   "metadata": {},
   "source": [
    "# Examples of Classic ML models "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70e057ac",
   "metadata": {},
   "source": [
    "Finally, we will compare others descriptros such as AAcomposition and Composition,transition and distribution of AA (https://www.pnas.org/content/92/19/8700)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "bfae73b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from propy import PyPro"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8daeeb65",
   "metadata": {},
   "source": [
    "In the following cell, we are creating and pyPro Object based on the protein sequence. Pypro allows us the calculation of amino acid composition vectors    \n",
    "Here we create a list with the aminoacido composition vector for each sequence used in the previous model.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "96b5b7c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np \n",
    "aaComplist = []\n",
    "CTDList =[]\n",
    "for seq in seq_list:\n",
    "    Obj = PyPro.GetProDes(seq)\n",
    "    aaComplist.append(np.array(list(Obj.GetAAComp().values())))\n",
    "    CTDList.append(np.array(list(Obj.GetCTD().values())))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "9b5e54b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "dc_dataset_aacomp = dc.data.NumpyDataset(X=aaComplist,y=deltaTm)\n",
    "dc_dataset_ctd = dc.data.NumpyDataset(X=CTDList,y=deltaTm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e52c9566",
   "metadata": {},
   "source": [
    "# Evaluation of classical machine learning models "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3dd6f56",
   "metadata": {},
   "source": [
    "In the following cell we create a randomForest Regressor and the deepchem SklearnModel. As it was used in the DL models, here we use \"MAE\" score to evaluate the results of the regression "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "1f8b8466",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RandomForestRegressor\n",
      "Train score is : {'mae_score': 1.7916551501995608}\n",
      "Test score is : {'mae_score': 3.8967191996673947}\n"
     ]
    }
   ],
   "source": [
    "from deepchem import splits\n",
    "splitter = splits.RandomSplitter()\n",
    "train, test  = splitter.train_test_split(dc_dataset_aacomp,seed=42)\n",
    "from sklearn.ensemble import RandomForestRegressor\n",
    "from deepchem.utils.evaluate import Evaluator\n",
    "import pandas as pd\n",
    "print(\"RandomForestRegressor\")\n",
    "seed = 42 # Set a random seed to get stable results\n",
    "sklearn_model = RandomForestRegressor(n_estimators=100, max_features='sqrt')\n",
    "sklearn_model.random_state = seed\n",
    "model = dc.models.SklearnModel(sklearn_model)\n",
    "model.fit(train)\n",
    "metric = dc.metrics.Metric(dc.metrics.mae_score)\n",
    "train_score = model.evaluate(train, [metric])\n",
    "test_score = model.evaluate(test, [metric])\n",
    "print(\"Train score is : {}\".format(train_score))\n",
    "print(\"Test score is : {}\".format(test_score))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "782dd98f",
   "metadata": {},
   "source": [
    "In the following cell we create a Suport Vector Regressor and the deepchem SklearnModel. As it was used in the DL models, here we use \"MAE\" score to evaluate the results of the regression "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "b8b28756",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SupportVectorMachineRegressor\n",
      "Train score is : {'mae_score': 3.275727325767219}\n",
      "Test score is : {'mae_score': 4.058136267284038}\n"
     ]
    }
   ],
   "source": [
    "print(\"SupportVectorMachineRegressor\")\n",
    "from sklearn.svm import SVR\n",
    "svr_sklearn = SVR(kernel=\"poly\",degree=4)\n",
    "svr_sklearn.random_state = seed \n",
    "model = dc.models.SklearnModel(svr_sklearn)\n",
    "model.fit(train)\n",
    "metric = dc.metrics.Metric(dc.metrics.mae_score)\n",
    "train_score = model.evaluate(train, [metric])\n",
    "test_score = model.evaluate(test, [metric])\n",
    "print(\"Train score is : {}\".format(train_score))\n",
    "print(\"Test score is : {}\".format(test_score))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a423530a",
   "metadata": {},
   "source": [
    "# Congratulations! Time to join the Community!\n",
    "\n",
    "Congratulations on completing this tutorial notebook! If you enjoyed working through the tutorial, and want to continue working with DeepChem, we encourage you to finish the rest of the tutorials in this series. You can also help the DeepChem community in the following ways:\n",
    "\n",
    "## Star DeepChem on [GitHub](https://github.com/deepchem/deepchem)\n",
    "This helps build awareness of the DeepChem project and the tools for open source drug discovery that we're trying to build.\n",
    "\n",
    "## Join the DeepChem Gitter\n",
    "The DeepChem [Gitter](https://gitter.im/deepchem/Lobby) hosts a number of scientists, developers, and enthusiasts interested in deep learning for the life sciences. Join the conversation!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "deepchem",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  },
  "toc-autonumbering": false,
  "toc-showcode": false,
  "toc-showmarkdowntxt": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
